<?php

namespace App\ServicesNew\Impl;

use App\BO\Order\CreateOrder\Exceptions\InvalidAddressException;
use App\BO\Order\CreateOrder\Exceptions\InvalidServiceAreaException;
use App\BO\Order\CreateOrderItem;
use App\BO\OrderStatus\OrderStateDefinition;
use App\BO\OrderStatus\OrderStatus;
use App\BO\Product\ProductSkuServiceAreaBO;
use App\Entities\CouponCodeModel;
use App\Entities\GoodsPaymentEntity;
use App\Entities\OrderEntity;
use App\Entities\OrderItemEntity;
use App\Entities\PaymentEntity;
use App\Entities\UserAddressModel;
use App\Models\UserCouponModel;
use App\ServicesNew\CartService;
use App\ServicesNew\CouponService;
use App\ServicesNew\CreateOrderService;
use App\ServicesNew\UserBalanceService;
use App\Utils\Assert;
use App\Utils\MoneyFormatUtil;
use App\Utils\TradeNoGeneratorUtil;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class CreateOrderServiceImpl implements CreateOrderService
{
    /**
     * @var CouponService
     */
    private $couponService;

    /**
     * @var UserBalanceService
     */
    private $balanceService;

    /**
     * @var CartService
     */
    private $cartService;

    public function __construct(CouponService $couponService, UserBalanceService $balanceService, CartService $cartService)
    {
        $this->couponService = $couponService;
        $this->balanceService = $balanceService;
        $this->cartService = $cartService;
    }

    /**
     * @inheritDoc
     */
    public function createOrder(CreateOrderItem $createOrderItem)
    {
        // 1. 校验订单信息
        $this->checkOrder($createOrderItem);

        DB::beginTransaction();

        // 2. 生成预订单（入库，但用户不可见）
        $orderEntity = $this->createPreOrder($createOrderItem);

        // 3. 扣除库存
        $this->changeRepertory();

        // 4. 优惠券操作：选择优惠券，计算价格
        $this->useCoupon($createOrderItem, $orderEntity);

        // 5. 余额抵扣操作：余额操作
        $this->useBalance($createOrderItem, $orderEntity);

        // 6. 确认订单
        $this->confirm($createOrderItem, $orderEntity);

        // 7. 删除购物车信息
        $this->clearCartItem($createOrderItem);

        // 提交事务
        DB::commit();
        Log::info('订单创建成功');

        return $orderEntity;
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @throws \Exception
     */
    private function checkOrder(CreateOrderItem $createOrderItem)
    {
        Log::info('开始订单检查');

        // 1. 下单地址与城市地址是否一致
        Assert::equals(
            $createOrderItem->getOrderAddress()->getProvinceId(),
            $createOrderItem->getCurrentAddress()->getId(),
            InvalidAddressException::class
        );

        // 2. 商品是否存在
        Assert::notNull($createOrderItem->getProduct());

        // 3. 产品区域检查
        if ($createOrderItem->isCheckProductRange()) {
            $hasService = (new ProductSkuServiceAreaBO(
                $createOrderItem->getOrderAddress(),
                $createOrderItem->getCartItemEntity()->getProductId()
            ))
                ->hasService();
            Assert::isTrue($hasService, InvalidServiceAreaException::class);
        }

        // 4. 单价检查
        // pass

        // 5. 商品数据是否合法
        // pass

        // 6. 优惠券检查
        if ($createOrderItem->useCoupon()) {
            $this->couponCheck($createOrderItem);
        }

        Log::info('创建订单检查通过');
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @return OrderEntity
     * @throws \Exception
     */
    private function createPreOrder(CreateOrderItem $createOrderItem)
    {
        $userAddressEntity = $createOrderItem->getOrderAddress();
        $serviceDate = $createOrderItem->getServiceDate();
        $product = $createOrderItem->getProduct();

        $orderEntity = OrderEntity::getNewInstance();
        $orderEntity->setOrderState(OrderStateDefinition::STATE_BEFORE_INIT);

        $orderEntity->setNo(TradeNoGeneratorUtil::get());

        // 渠道
        $orderEntity->setChannelId($createOrderItem->getChannelId());

        $orderEntity->setUserId($userAddressEntity->getUserId());
        $orderEntity->setFullAddress($userAddressEntity->getFullAddress());
        $orderEntity->setCityId($userAddressEntity->getProvinceId());
        $orderEntity->setContactName($userAddressEntity->getContactName());
        $orderEntity->setContactPhone($userAddressEntity->getContactPhone());

        $orderEntity->setProductId($product->getProductId());
        $orderEntity->setProductSkuId($product->getProductSkuId());
        $orderEntity->setProductUnitId($product->getProductUnitId());
        $orderEntity->setSkuShowType($product->getSkuShowType());
        $orderEntity->setSkuTitle($product->getSkuTitle());

        $orderEntity->setRemark($createOrderItem->getRemark());

        // 服务时间
        $orderEntity->setAssignDate($serviceDate->getDate());
        $orderEntity->setAssignTime($serviceDate->getTimePeriodId());

        $orderEntity->setCompanyId(UserAddressModel::getCompanyID($userAddressEntity->getProvinceId()));
        $orderEntity->setTotalAmount(MoneyFormatUtil::pennyToDollar($this->genTotalAmount($createOrderItem)));  // 订单总金额

        if ($createOrderItem->useCoupon()) {  // 如果有使用优惠券
            $orderEntity->setCouponCodeId($createOrderItem->getCouponEntity()->getId());
        }

        $orderEntity->save();
        $orderEntity->refresh();

        Log::info('生成预订单成功，order ', $orderEntity->toArray());
        return $orderEntity;
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @return int
     * @throws \Exception
     */
    private function genTotalAmount(CreateOrderItem $createOrderItem)
    {
        $productInfo = $createOrderItem->getProduct();

        // 购买数量
        $unit = $createOrderItem->getCartItemEntity()->getSquareMetre();

        if ($createOrderItem->useCoupon()) {
            $couponCodeEntity = $createOrderItem->getCouponEntity();
            Assert::notNull($couponCodeEntity);

            $price = $productInfo->getOldPrice();
            $createOrderItem->setProductSkuCalculatePrice($price);

            $total = $price * $unit;

            // 固定金额优惠券
            if (CouponCodeModel::TYPE_FIXED == $couponCodeEntity->getType()) {
                $couponValue = MoneyFormatUtil::dollarToPenny($couponCodeEntity->getValue());
                $createOrderItem->setCouponDiscountValue($couponValue);
                return max(0, $total - $couponValue);
            }

            // 比例优惠券
            if (CouponCodeModel::TYPE_PERCENT == $couponCodeEntity->getType()) {
                $discountTotal = (int)($total * ($couponCodeEntity->getValue() / 10));
                $createOrderItem->setCouponDiscountValue($total - $discountTotal);
                return $discountTotal;
            }

            return $total;

        } else {
            $price = $productInfo->getPrice();
            $createOrderItem->setProductSkuCalculatePrice($price);
            return $price * $unit;
        }
    }

    /**
     * 修改库存
     */
    private function changeRepertory()
    {
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @param OrderEntity $orderEntity
     * @throws \Exception
     */
    private function useCoupon(CreateOrderItem $createOrderItem, OrderEntity $orderEntity)
    {
        if (!$createOrderItem->useCoupon()) {
            return;
        }

        $userCouponEntity = $createOrderItem->getUserCouponEntity();
        Assert::notNull($userCouponEntity);
        $userCouponEntity->setUsed(UserCouponModel::USED);
        $userCouponEntity->setOrderId($orderEntity->getId());
        $userCouponEntity->save();
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @param OrderEntity $orderEntity
     */
    private function useBalance(CreateOrderItem $createOrderItem, OrderEntity $orderEntity)
    {
        if (!$createOrderItem->useBalance()) {
            return;
        }

        $userId = $orderEntity->getUserId();
        $amount = $this->balanceService->getUseAbleAmountByUid($userId);

        if ($amount > 0) {

            $totalAmount = MoneyFormatUtil::dollarToPenny($orderEntity->getTotalAmount());
            $useAmount = min($totalAmount, $amount);

            $createOrderItem->setUseBalanceAmount($useAmount);

            $transactionRecordId = $this->balanceService->use($userId, $useAmount);

            // 修改订单金额
            $orderEntity->setTransactionRecordId($transactionRecordId);
            $orderEntity->setTotalAmount(MoneyFormatUtil::pennyToDollar($totalAmount - $useAmount));
            $orderEntity->save();

            // 使用账户余额
            Log::info('账户余额使用成功:' . $useAmount);
        }
    }

    /**
     * @param CreateOrderItem $createOrderItem
     * @param OrderEntity $orderEntity
     */
    private function confirm(CreateOrderItem $createOrderItem, OrderEntity $orderEntity)
    {
        DB::beginTransaction();

        // 1. 增加 goods_payment 记录
        $goodsPayment = GoodsPaymentEntity::getNewInstance();
        $goodsPayment->setUserId($orderEntity->getUserId());
        $goodsPayment->setGoodsName($orderEntity->getSkuTitle());
        $goodsPayment->setOrderId($orderEntity->getId());
        $goodsPayment->setTradeNo($orderEntity->getNo());
        $goodsPayment->setTransactionRecordId($orderEntity->getTransactionRecordId());
        $goodsPayment->setUseBalance($createOrderItem->getUseBalanceAmount());
        $goodsPayment->setCouponCodeId($orderEntity->getCouponCodeId());
        $goodsPayment->setCouponDiscountValue($createOrderItem->getCouponDiscountValue());
        $goodsPayment->setProductSkuId($orderEntity->getProductSkuId());
        $goodsPayment->setProductSkuPrice($createOrderItem->getProductSkuCalculatePrice());
        $goodsPayment->setNum($createOrderItem->getCartItemEntity()->getSquareMetre());
        $goodsPayment->setAmount(MoneyFormatUtil::dollarToPenny($orderEntity->getTotalAmount()));
        $goodsPayment->setIsMaster(true);
        $goodsPayment->save();

        // 2. 增加 OrderItem
        $orderItemEntity = OrderItemEntity::getNewInstance();
        $orderItemEntity->setOrderId($orderEntity->getId());
        $orderItemEntity->setProductId($orderEntity->getProductId());
        $orderItemEntity->setProductSkuId($orderEntity->getProductSkuId());
        $orderItemEntity->setSquareMetre($createOrderItem->getCartItemEntity()->getSquareMetre());
        $orderItemEntity->setPrice($orderEntity->getTotalAmount());
        $orderItemEntity->save();

        // 3. 增加 payment 记录
        $payment = PaymentEntity::getNewInstance();
        $payment->setState(OrderStateDefinition::STATE_WAITING_PAY);
        $payment->setTradeNo($orderEntity->getNo());
        $payment->setAmount(MoneyFormatUtil::dollarToPenny($orderEntity->getTotalAmount()));
        $payment->save();

        // 4. 将 Order 状态初始化
        $orderStatus = new OrderStatus($orderEntity);
        $orderStatus->getOrderStateContext()->init();

        DB::commit();
    }

    /**
     * @param CreateOrderItem $createOrderItem
     */
    private function clearCartItem(CreateOrderItem $createOrderItem)
    {
        $this->cartService->clearUserCartItem($createOrderItem->getUserEntity()->getUserId());
    }

    /**
     * 优惠券使用检查
     * @param CreateOrderItem $createOrderItem
     * @throws \Exception
     */
    private function couponCheck(CreateOrderItem $createOrderItem)
    {
        $couponCodeEntity = $createOrderItem->getCouponEntity();
        Assert::notNull($couponCodeEntity, null, '优惠券不存在');

        $pass = $this->cartService->useAbleCouponCheckByCartItemEntity($createOrderItem->getCartItemEntity(), $couponCodeEntity);
        Assert::isTrue($pass, null, '优惠券不可以使用');
    }
}
